#pragma once

#include <memory>
#include <unordered_map>
#include "NetBuffer.h"
#include "NetPacket.h"
#include "MultiBufferQueue.h"

class SessionManager;
class Connection;

enum SessionHandleStatus {
    SessionHandleSuccess,
    SessionHandleCapture,
    SessionHandleUnhandle,
    SessionHandleWarning,
    SessionHandleError,
    SessionHandleKill,
};

class Session
{
public:
    enum Status {
        Idle,
        Running,
        Disabled,
    };

    Session(const std::string &name);
    virtual ~Session();

    void ConnectServer(const std::string &address, const std::string &port);

    void SetConnection(std::shared_ptr<Connection> &&conn);
    const std::shared_ptr<Connection> &GetConnection() const;

    bool GrabShutdownFlag();
    bool IsShutdownExpired() const;

    void Update();
    virtual int HandlePacket(INetPacket *pck) = 0;

    virtual void PushRecvPacket(INetPacket *pck);

    virtual void PushSendPacket(const INetPacket &pck);
    virtual void PushSendPacket(const std::string_view &data);
    virtual void PushSendPacket(const INetPacket &pck, const INetPacket &data);
    virtual void PushSendPacket(const INetPacket &pck, const std::string_view &data);
    virtual void PushSendPacket(const INetPacket *pcks[], size_t pck_num,
                                const std::string_view datas[], size_t data_num);

    virtual void KillSession();
    virtual void ShutdownSession();
    virtual void OnShutdownSession();
    virtual void DeleteObject();

    virtual void OnTick();
    virtual void OnConnected();
    virtual void OnManaged();

    virtual void Disconnect();
    virtual bool IsIndependent() const;
    virtual bool HasSendDataAwaiting() const;
    virtual size_t GetSendDataSize() const;

    virtual int GetConnectionLoadValue() const;

    const std::string &GetHost() const;
    unsigned long GetIPv4() const;
    unsigned short GetPort() const;

    void SetManager(SessionManager *manager) { manager_ = manager;}
    SessionManager *GetManager() const { return manager_; }

    void SetStatus(Status status) { status_ = status; }
    bool IsStatus(Status status) const { return status_ == status; }
    void Disable() { status_ = Disabled; }
    bool IsActive() const { return status_ != Disabled; }

    uint64 last_recv_pck_time() const { return last_recv_pck_time_; }
    uint64 last_send_pck_time() const { return last_send_pck_time_; }

protected:
    virtual void OnRecvPacket(INetPacket *pck);

    void ClearShutdownFlag();
    void ClearRecvPacket();

    void set_overflow_packet_max_size(size_t size) {
        overflow_packet_max_size_ = size;
    }

private:
    class LargePacketHelper;
    void PushRecvFragmentPacket(INetPacket *pck);
    void PushSendOverflowPacket(const INetPacket &pck);
    void PushSendOverflowPacket(const INetPacket &pck, const INetPacket &data);
    void PushSendOverflowPacket(const INetPacket &pck, const std::string_view &data);
    void PushSendOverflowPacket(size_t packet_total_size,
                                const INetPacket *pcks[], size_t pck_num,
                                const std::string_view datas[], size_t data_num);
    void PushSendFragmentPacket(uint32 opcode, ConstNetBuffer datas[], size_t count);

    const std::string name_;
    Status status_;
    SessionManager *manager_;

    std::shared_ptr<Connection> connection_;
    MultiBufferQueue<INetPacket*, 1024> recv_queue_;

    std::mutex fragment_mutex_;
    std::unordered_map<uint32, INetPacket*> fragment_packets_;
    std::atomic<uint32> overflow_packet_count_;
    size_t overflow_packet_max_size_;

    std::atomic<time_t> shutdown_time_;
    uint64 last_recv_pck_time_, last_send_pck_time_;
};
